using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Collections.Generic;

namespace GraphMyCode.Controls
{
	[TemplatePart(Type = typeof(Path), Name = "PART_Content")]
	public class GraphItem : Control
	{
		public static readonly DependencyProperty DataTemplateProperty =
			DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(GraphItem), null);

		public static readonly DependencyProperty ItemDescriptorProperty =
			DependencyProperty.Register("ItemDescriptor", typeof(GraphItemDescriptor), typeof(GraphItem),
			                            new PropertyMetadata(null, ItemDescriptorPropertyChanged));

		internal Rect Bounds;

		private ContentPresenter content;
		private bool isFiltered;
		private bool isHighlighted = true;

		private bool isMouseOver;
		private bool isNextToMouseOverItem;
		private bool isSelected;
		private GraphControl parentGraph;
		//private TranslateTransform movingTransform;

		public GraphItem()
		{
			DefaultStyleKey = typeof(GraphItem);
			SizeChanged +=
				(sender, e) =>
					{
						Bounds.Width = e.NewSize.Width;
						Bounds.Height = e.NewSize.Height;
					};
			//MovingTransform = new TranslateTransform();
		}

		public GraphItemDescriptor ItemDescriptor
		{
			get { return (GraphItemDescriptor)GetValue(ItemDescriptorProperty); }
			set { SetValue(ItemDescriptorProperty, value); }
		}

		public ContentPresenter Content
		{
			get { return content; }
			set
			{
				content = value;

				if (!IsEdgeItem)
				{
					var nodeDescriptor = (GraphNodeDescriptor)ItemDescriptor;
					if (DataTemplate == null)
						content.Content = nodeDescriptor.GetVisualPath();
					else
						content.ContentTemplate = DataTemplate;
				}
			}
		}

		public GraphControl ParentGraph
		{
			get { return parentGraph; }
			set { parentGraph = value; }
		}

		public bool IsSelected
		{
			get { return isSelected; }
			set
			{
				if (isSelected != value)
				{
					isSelected = value;
					if (isSelected)
						parentGraph.Select(this);
					else
						parentGraph.Unselect(this);
					UpdateStates();
				}
			}
		}

		public DataTemplate DataTemplate
		{
			get { return (DataTemplate)GetValue(DataTemplateProperty); }
			set { SetValue(DataTemplateProperty, value); }
		}

		public bool IsEdgeItem { get; private set; }

		public bool IsHighlighted
		{
			get { return isHighlighted; }
			set
			{
				if (isHighlighted != value)
				{
					isHighlighted = value;
					UpdateStates();
				}
			}
		}


		public bool IsFiltered
		{
			get { return isFiltered; }
			set
			{
				if (isFiltered != value)
				{
					isFiltered = value;
					UpdateStates();
				}
			}
		}

		//public TranslateTransform MovingTransform
		//{
		//    get { return movingTransform; }
		//    set
		//    {
		//        movingTransform = value;
		//        RenderTransform = movingTransform;
		//    }
		//}

		internal void Unselect()
		{
			isSelected = false;
			UpdateStates();
		}

		internal void Select()
		{
			isSelected = true;
			UpdateStates();
		}

		private static void ItemDescriptorPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			var descriptor = e.NewValue as GraphItemDescriptor;
			var item = obj as GraphItem;

			if (item != null) 
				item.IsEdgeItem = descriptor is GraphEdgeDescriptor;
		}

		protected override void OnMouseEnter(MouseEventArgs e)
		{
			if (!parentGraph.IsDraggingSpaceEnabled && !IsEdgeItem && isHighlighted)
			{
				isMouseOver = true;
				UpdateStates();

				var nodeDesc = (GraphNodeDescriptor)ItemDescriptor;

				foreach (GraphItem edge in nodeDesc.InEdges.Union(nodeDesc.OutEdges))
				{
					edge.isNextToMouseOverItem = true;
					edge.UpdateStates();
				}
			}
			base.OnMouseEnter(e);
		}

		protected override void OnMouseLeave(MouseEventArgs e)
		{
			if (!parentGraph.IsDraggingSpaceEnabled && !IsEdgeItem)
			{
				isMouseOver = false;
				UpdateStates();

				var nodeDesc = (GraphNodeDescriptor)ItemDescriptor;

				foreach (GraphItem edge in nodeDesc.InEdges.Union(nodeDesc.OutEdges))
				{
					edge.isNextToMouseOverItem = false;
					edge.UpdateStates();
				}
			}
			base.OnMouseLeave(e);
		}

		protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
		{
			if (!parentGraph.IsDraggingSpaceEnabled && !IsEdgeItem && !IsSelected)
			{
				parentGraph.ClearSelection(true);
				Select();
				parentGraph.SelectSingleItem(this);
				UpdateStates();
			}
			base.OnMouseLeftButtonDown(e);
		}

		protected void UpdateStates()
		{
			if (isFiltered)
				VisualStateManager.GoToState(this, "Hidden", true);
			else
			{
				VisualStateManager.GoToState(this, "Visible", true);

				if (isSelected)
					VisualStateManager.GoToState(this, "Selected", true);
				else VisualStateManager.GoToState(this, "NotSelected", true);

				if (isHighlighted)
					VisualStateManager.GoToState(this, "Highlighted", true);
				else VisualStateManager.GoToState(this, "NotHighlighted", true);

				if (isMouseOver)
					VisualStateManager.GoToState(this, "MouseOver", true);
				else if (!IsEdgeItem)
					VisualStateManager.GoToState(this, "NormalNode", true);

				if (isNextToMouseOverItem)
					VisualStateManager.GoToState(this, "NextToMouseOver", true);
				else if (IsEdgeItem)
					VisualStateManager.GoToState(this, "NormalEdge", true);
			}
		}

		public override void OnApplyTemplate()
		{
			Content = GetTemplateChild("PART_Content") as ContentPresenter;

			UpdateStates();
			ItemDescriptor.UpdatePosition();
		}

		public void SelectAdjacent()
		{
			IsSelected = true;

			foreach (GraphItem node in GetAdjacentNodes())
				node.IsSelected = true;
		}

		public IEnumerable<GraphItem> GetAdjacentNodes()
		{
			var nodeDesc = (GraphNodeDescriptor)ItemDescriptor;

			return nodeDesc.InEdges.Where(e => !e.IsFiltered).Select(e => ((GraphEdgeDescriptor)e.ItemDescriptor).FromItem).
				Union(
				nodeDesc.OutEdges.Where(e => !e.IsFiltered).Select(e => ((GraphEdgeDescriptor)e.ItemDescriptor).ToItem));
		}
	}
}